#include "KinectHandler.h"
#include<iostream>
#include<qthread.h>
#include<algorithm>
#include "devicemanager.h"

//constructor
KinectHandler::KinectHandler(DeviceManager *dm) {
	m_deviceManager = dm;
	m_hasPrimary = false;
	m_hasSecondary = false;
	m_primaryID = -1;
	m_secondaryID = -1;  
	m_primaryRed = false; 
	m_secondaryRed = false;
	m_primaryRegion = LEFT_REGION;
	m_secondaryRegion = LEFT_REGION;
}

//receives coords from Listener and forwards info to proper component depending on state
void KinectHandler::receivePointUpdate(double x, double y, double z, int id) {

	//convert the coords which are in millimeters to a 0-1 range
	double newX = (std::max)(0.0, (std::min)((x+COORD_SCALE_X)/(2*COORD_SCALE_X), 1.0));
	double newY = (std::max)(0.0, (std::min)((y+COORD_SCALE_Y)/(2*COORD_SCALE_Y), 1.0));
	double newZ = (z+COORD_SCALE_Z)/(2*COORD_SCALE_Z);

	//check for swipes
	if(id == m_primaryID) {
		if(newX < 0.2) {
			if(m_primaryRegion == MIDDLE_REGION) {
				emit signalBrowseLoadMenu(-1); 
				emit signalBrowseImportMenu(-1);
			}
			m_primaryRegion = LEFT_REGION;
		}
		else if(newX > 0.8) {
			if(m_primaryRegion == MIDDLE_REGION) {
				emit signalBrowseLoadMenu(1); 
				emit signalBrowseImportMenu(1);
			}
			m_primaryRegion = RIGHT_REGION;
		}
		else {
			m_primaryRegion = MIDDLE_REGION;
		}
	}
	if(id == m_secondaryID) {
		if(newX < 0.2) {
			if(m_secondaryRegion == MIDDLE_REGION) {
				emit signalBrowseLoadMenu(-1); 
				emit signalBrowseImportMenu(-1);
			}
			m_secondaryRegion = LEFT_REGION;
		}
		else if(newX > 0.8) {
			if(m_secondaryRegion == MIDDLE_REGION) {
				emit signalBrowseLoadMenu(1); 
				emit signalBrowseImportMenu(1);
			}
			m_secondaryRegion = RIGHT_REGION;
		}
		else {
			m_secondaryRegion = MIDDLE_REGION;
		}
	}
	
	//check if newX or newY are at edges of window in order to set the cursor red
	if(newX == 0.0 || newX == 1.0 || newY == 0.0|| newY == 1.0) {
		if(id == m_primaryID && !m_primaryRed) {
			m_primaryRed = true;
			emit signalSetCursorRed(1);
		}
		if(id == m_secondaryID && !m_secondaryRed) {
			m_secondaryRed = true;
			emit signalSetCursorRed(2);
		}
	}
	//otherwise put the cursors back to normal if they were red
	else {
		if(id == m_primaryID && m_primaryRed) {
			m_primaryRed = false;
			if(m_inputs[1].state() == FREE_MOTION) {
				emit signalRevertCursor1();
			}
			else {
				emit signalSetCursorGreen(1);
			}
		}
		if(id == m_secondaryID && m_secondaryRed) {
			m_secondaryRed = false;
			if(m_inputs[2].state() == FREE_MOTION) {
				emit signalRevertCursor2();
			}
			else {
				emit signalSetCursorGreen(2);
			}
		}
	}

	//if it is the primary cursor and the secondary is either free to move or doesn't exist, emit the move signal
	if((id ==  m_primaryID && m_inputs[2].state() == FREE_MOTION) || (id ==  m_primaryID && !m_hasSecondary)){
		double dx = newX - m_inputs[1].x();
		double dy = newY - m_inputs[1].y();
		emit signalMove(dx, dy);
	}
	//else if it is the secondary cursor and the free one is free to move, emit the move signal
	else if(id ==  m_secondaryID && m_inputs[1].state() == FREE_MOTION){
		double dx = newX - m_inputs[2].x();
		double dy = newY - m_inputs[2].y();
		emit signalMove(dx, dy);
	}
	//else if both cursors are selected on the left and right edge, perform horizontal skewing
	else if ((m_inputs[1].state() == SELECT_E && m_inputs[2].state() == SELECT_W) ||
			 (m_inputs[2].state() == SELECT_E && m_inputs[1].state() == SELECT_W) ){

		//save the current state of the picture as the reference point
		if(!m_isTransforming){ 
			m_isTransforming = true;
			emit signalSaveState();
		}

		//find the difference and angle between the z values
		qreal zDiff = fabs(m_inputs[2].z() - m_inputs[1].z());
		qreal angleX = zDiff*90;

		//flip the angle depending on which point is in front
		if ((m_inputs[1].z() < m_inputs[2].z() && m_inputs[1].x() >  m_inputs[2].x()) || (m_inputs[1].z() >  m_inputs[2].z() && m_inputs[1].x() <  m_inputs[2].x())){
			angleX = -angleX;
		}

		//emit the rotate signal about the x-axis
		emit signalRotate(angleX, Qt::XAxis);
	} 
	//else if the cursors are selected on the north and south edges of a picture
	else if ((m_inputs[1].state() == SELECT_N && m_inputs[2].state() == SELECT_S) ||
			 (m_inputs[2].state() == SELECT_N && m_inputs[1].state() == SELECT_S) ){

		//save the picture's current state as a reference point
		if(!m_isTransforming){ 
			m_isTransforming = true;
			emit signalSaveState();
		}

		//find the difference and angle between z values
		qreal zDiff = fabs(m_inputs[2].z() - m_inputs[1].z());
		qreal angleY = -zDiff*90;

		//flip the angle depending which point is in front
		if ((m_inputs[1].z() <  m_inputs[2].z() && m_inputs[1].y() >  m_inputs[2].y()) || (m_inputs[1].z() >  m_inputs[2].z() && m_inputs[1].y() <  m_inputs[2].y())){
			angleY = -angleY;
		}

		//emit the rotate signal about the y axis
		emit signalRotate(angleY, Qt::YAxis);
	} 
	//else if both cursors are in a select state, perform the scale, rotate, move transform
	else if(m_inputs[1].state() != FREE_MOTION && m_inputs[2].state() != FREE_MOTION) {

		//save the pictures current state to use as reference
		if(!m_isTransforming){ 
			m_isTransforming = true;
			emit signalPrepareTransform(m_inputs[1].x(), m_inputs[1].y(), m_inputs[1].z(), m_inputs[2].x(), m_inputs[2].y(), m_inputs[2].z());
		}

		//emit the transform signal
		emit signalTransform(m_inputs[1].x(), m_inputs[1].y(), m_inputs[1].z(), m_inputs[2].x(), m_inputs[2].y(), m_inputs[2].z());
	}
	
	//update the cursor positions
	if(id == m_primaryID) {
		m_inputs[1].setX(newX);
		m_inputs[1].setY(newY);
		m_inputs[1].setZ(newZ);
		emit signalUpdateCursor(m_inputs[1]);
	}
	else if(id == m_secondaryID) {
		m_inputs[2].setX(newX);
		m_inputs[2].setY(newY);
		m_inputs[2].setZ(newZ);
		emit signalUpdateCursor(m_inputs[2]);
	}
	else {
		return;
	}
}

//called from listener when it detects either hand pushing
void KinectHandler::receivePush(int pointNumber) {
	//if it is the primary hand
	if (pointNumber == 1 && m_hasPrimary) {
		//if the hand is in free motion, emit select, else emit deselect
		if(m_inputs[1].state() == FREE_MOTION) {
			emit signalSelect(m_inputs[1]);
		}
		else {
			emit signalDeselect(m_inputs[1]);
			emit signalDeselect(m_inputs[2]);
		}
	}
	//else if it is the secondary hand
	else if (pointNumber == 2 && m_hasSecondary) {
		//if it is free to move, select, else deselect
		if(m_inputs[2].state() == FREE_MOTION) {
			emit signalSelect(m_inputs[2]);
		}
		else {
			emit signalDeselect(m_inputs[2]);
			emit signalDeselect(m_inputs[1]);
		}
	}
	else {}

	//if the shape was transforming, end the transformation
	if(m_isTransforming){
		m_isTransforming = false;
	}
}

//called when listener detects a new hand point
void KinectHandler::receivePointCreate(int id) {
	//if there are no points, create a primary
	if(!m_hasPrimary) {
		m_primaryID = id;
		m_hasPrimary = true;
		InputPoint p(-1,-1,-1,1);
		m_inputs[1] = p;
		emit signalAddedInput(p);
	}
	//else if there is a primary but no secondary, make one
	else if(!m_hasSecondary) {
		m_secondaryID = id;
		m_hasSecondary = true;
		InputPoint p(-1,-1,-1,2);
		m_inputs[2] = p;
		emit signalAddedInput(p);
	}
	else {
		//replace the secondary point; this shouldn't happen too often
		emit signalDeselect(m_inputs[2]);
		m_inputs.erase(2);
		m_secondaryID = -1;
		m_hasSecondary = false;
		emit signalRemovedInput(2, false);

		m_secondaryID = id;
		m_hasSecondary = true;
		InputPoint p(-1,-1,-1,2);
		m_inputs[2] = p;
		emit signalAddedInput(p);
	}
}

//called when the listener loses a hand point
void KinectHandler::receivePointDestroy(int id) {
	//if the primary point was lost
	if(m_hasPrimary && id == m_primaryID) {
		//if there is a secondary, make it the primary
		if(m_hasSecondary) {
			m_primaryID = m_secondaryID;
			emit signalDeselect(m_inputs[2]);
			m_inputs.erase(2);
			m_secondaryID = -1;
			m_hasSecondary = false;
			emit signalRemovedInput(2, true);
		}
		//otherwise, remove the primary completely
		else {
			m_hasPrimary = false;
			emit signalDeselect(m_inputs[1]);
			m_primaryID = -1;
			m_inputs.erase(1);
			emit signalRemovedInput(1, false);
		}
	}
	//else if the secondary was lost, destroy it
	else if(m_hasSecondary && id == m_secondaryID) {
		emit signalDeselect(m_inputs[2]);
		m_inputs.erase(2);
		m_secondaryID = -1;
		m_hasSecondary = false;
		emit signalRemovedInput(2, false);
	}
	else {}
}

//called from Desk when a hand point changes state
void KinectHandler::slotUpdateInputPoint(int id, InputState state) {
	if(id == 1) {
		m_inputs[1].setState(state);
	}
	else if(id == 2) {
		m_inputs[2].setState(state);
	}
	else {}
}

//slot called when the Kinect is first detected
void KinectHandler::receiveKinectConnected() {
	//start the manager's timer
	m_deviceManager->startTimer();
	emit signalDeviceConnected();
}
